package com.yc.util;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.yc.hwc.biz.BizException;

import ognl.MemberAccess;
import ognl.Ognl;
import ognl.OgnlException;

public class Utils {

	public static <T> T clone(T t, String... fields) {
		if (t == null) {
			return null;
		}
		try {
			@SuppressWarnings("unchecked")
			T tt = (T) t.getClass().newInstance();
			for (String f : fields) {
				Field fd = t.getClass().getDeclaredField(f);
				fd.setAccessible(true);
				fd.set(tt, fd.get(t));
			}
			return tt;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void test(boolean bool, String msg) throws BizException {
		if (bool) {
			throw new BizException(msg, -99, null);
		}
	}

	@SafeVarargs
	public static <T> T nvl(T... objs) {
		for (T o : objs) {
			if (o != null) {
				return o;
			}
		}
		return null;
	}

	public static <T> T setBean(T t, Object... fieldAndValues) {
		@SuppressWarnings("rawtypes")
		Map ognlContent = Ognl.createDefaultContext(t, DEFAULT_MEMBER_ACCESS);
		for (int i = 0; i < fieldAndValues.length; i += 2) {
			if (i + 1 == fieldAndValues.length) {
				break;
			}
			try {
				Ognl.setValue(fieldAndValues[i].toString(), ognlContent, t, fieldAndValues[i + 1]);
			} catch (OgnlException e) {
				throw new RuntimeException(e);
			}
		}
		return t;
	}

	public static Map<?, ?> map(Object... objs) {
		Map<Object, Object> ret = new HashMap<Object, Object>();
		for (int i = 0; i < objs.length; i += 2) {
			Object key = objs[i];
			Object val = i + 1 == objs.length ? null : objs[i + 1];
			ret.put(key, val);
		}
		return ret;
	}

	private final static DefaultMemberAccess DEFAULT_MEMBER_ACCESS = new DefaultMemberAccess(true);

	@SuppressWarnings("rawtypes")
	public static class DefaultMemberAccess implements MemberAccess {
		public boolean allowPrivateAccess = false;
		public boolean allowProtectedAccess = false;
		public boolean allowPackageProtectedAccess = false;

		/*
		 * ===================================================================
		 * Constructors
		 * ===================================================================
		 */
		public DefaultMemberAccess(boolean allowAllAccess) {
			this(allowAllAccess, allowAllAccess, allowAllAccess);
		}

		public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess,
				boolean allowPackageProtectedAccess) {
			super();
			this.allowPrivateAccess = allowPrivateAccess;
			this.allowProtectedAccess = allowProtectedAccess;
			this.allowPackageProtectedAccess = allowPackageProtectedAccess;
		}

		/*
		 * =================================================================== Public
		 * methods ===================================================================
		 */
		public boolean getAllowPrivateAccess() {
			return allowPrivateAccess;
		}

		public void setAllowPrivateAccess(boolean value) {
			allowPrivateAccess = value;
		}

		public boolean getAllowProtectedAccess() {
			return allowProtectedAccess;
		}

		public void setAllowProtectedAccess(boolean value) {
			allowProtectedAccess = value;
		}

		public boolean getAllowPackageProtectedAccess() {
			return allowPackageProtectedAccess;
		}

		public void setAllowPackageProtectedAccess(boolean value) {
			allowPackageProtectedAccess = value;
		}

		/*
		 * ===================================================================
		 * MemberAccess interface
		 * ===================================================================
		 */
		public Object setup(Map context, Object target, Member member, String propertyName) {
			Object result = null;

			if (isAccessible(context, target, member, propertyName)) {
				AccessibleObject accessible = (AccessibleObject) member;

				if (!accessible.isAccessible()) {
					result = Boolean.FALSE;
					accessible.setAccessible(true);
				}
			}
			return result;
		}

		public void restore(Map context, Object target, Member member, String propertyName, Object state) {
			if (state != null) {
				((AccessibleObject) member).setAccessible(((Boolean) state).booleanValue());
			}
		}

		/**
		 * Returns true if the given member is accessible or can be made accessible by
		 * this object.
		 */
		public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
			int modifiers = member.getModifiers();
			boolean result = Modifier.isPublic(modifiers);
			if (!result) {
				if (Modifier.isPrivate(modifiers)) {
					result = getAllowPrivateAccess();
				} else {
					if (Modifier.isProtected(modifiers)) {
						result = getAllowProtectedAccess();
					} else {
						result = getAllowPackageProtectedAccess();
					}
				}
			}
			try {
				Field propertyField = member.getDeclaringClass().getDeclaredField(propertyName);
				if (propertyField.getType().getName().startsWith("java") == false) {
					propertyField.setAccessible(true);
					Object memberObj = propertyField.get(target);
					if (memberObj == null) {
						Constructor c = propertyField.getType().getConstructor();
						memberObj = c.newInstance();
						propertyField.set(target, memberObj);
					}
				}
			} catch (NoSuchMethodException | SecurityException e) {
				e.printStackTrace();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			return result;
		}
	}

	public static Map<String, Object> asMap(Object obj, String... excludeKeys) {
		Map<String, Object> ret = new HashMap<String, Object>();
		Set<String> set = new HashSet<>();
		Collections.addAll(set, excludeKeys);
		if (obj != null) {
			for (Field f : obj.getClass().getDeclaredFields()) {
				if (set.contains(f.getName())) {
					continue;
				}
				String getter = "get" + Character.toUpperCase(f.getName().charAt(0)) + f.getName().substring(1);
				try {
					Method getterMethod = obj.getClass().getMethod(getter);
					ret.put(f.getName(), getterMethod.invoke(obj));
				} catch (NoSuchMethodException e) {
					continue;
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		}
		return ret;
	}

	public static Map<Object, Object> toMap(Object... objs) {
		Map<Object, Object> map = new HashMap<Object, Object>();
		for (int i = 0; i < objs.length - 1; i += 2) {
			map.put(objs[i], objs[i + 1]);
		}
		return map;
	}

	public static boolean isEmpty(Object obj) {
		return obj == null || (obj instanceof String && ((String) obj).trim().isEmpty());
	}

	/*
	 * public static void main(String[] args) { TaskLog ttt = new TaskLog();
	 * //ttt.setInUser(new User()); TaskLog tl = Utils.setBean(ttt,
	 * "inUser.inUser.id", 222, "task.course.id", 111); System.out.println(tl); }
	 */

}
